/*
 * @Author: lirl
 * @Date: 2022-08-08 16:47:40
 * @Description: 菜单相关处理方法
 */
import { AuthorityService } from 'jdf-jssdk';
import {
  transform,
  isEmpty,
  flatMap,
  map,
  partition
} from 'lodash-es';
import { isExternalLink, jsonParse } from 'jdf-util';
import settings from '@/settings/settings';
import { bizServiceURL } from '@/api/utils/constants';
import { alertErrorMsg } from '@/api/utils/pc-message';

/**
 * 获取菜单的扩展属性
 * @param {String} extendConfig 菜单扩展属性
 */
const getMenuExtend = (extendConfig) => jsonParse(extendConfig);

/**
 * 获取layout
 * @param {*} resolve
 */
const layoutModel = (layout) => resolve => require([`../../layout/${layout}`], resolve);

/**
 * 构建动态路由的component.
 */
const appMainModel = resolve => require(['../../views/app-main/AppMain'], resolve);

/**
 * 根据单个菜单信息构建路由.
 * @param {Object} menu 菜单数据
 * @param {String} menu.resource 菜单链接地址
 * @param {String} menu.resourceDesc 菜单名称
 * @param {String} menu.id 菜单id
 * @param {Number} menu.display 菜单是否展示 1:是 0：否
 * @return {Object} 路由信息
 * @return 路由组件的存放目录为当前菜单链接的最后两层所组成.，如当前链接为bpm/mywork/done,则component所存放的目录结构为views/mywork/done
 */
const constructOneMenuRoute = (menu) => {
  // 菜单名称、菜单pathId
  const {
    resource,
    resourceDesc,
    resourceCode,
    pathCode,
    pathDesc
  } = menu;
  /*
    1. 外链不构建路由
    2. 如果当前菜单有子菜单，并且没有配置可以点击，则不构造路由
  */
  if (isExternalLink(resource) || !(isEmpty(menu.children) || menu.extendObj.canClick)) {
    return null;
  }
  // 最顶级父级菜单的pathId，用于在跳转时判断该菜单属于哪个navbar
  const [topParentId] = pathCode.split('#');
  return {
    path: resource,
    component: appMainModel,
    name: resourceCode,
    meta: {
      pathId: topParentId,
      title: resourceDesc,
      breadcrumb: pathDesc,
      hasAuth: true,
      ...menu.extendObj,
    },
  };
};

/**
 * 构建子路由.
 * @param {Array} childs 子菜单列表
 */
const constructChildrenRoutes = (menu) => flatMap(menu.children, (item) => [
  constructOneMenuRoute(item),
  ...map(item.children, constructOneMenuRoute)
]);

/**
 * 构建顶级菜单路由.
 * @param {Object} menu 菜单数据
 * @param {String} menu.url 菜单链接地址
 * @param {String} menu.name 菜单名称
 * @param {String} menu.id 菜单id
 * @param {Number} menu.display 菜单是否展示 1:是 0：否
 * @returns {Object} 路由信息
 */
const constructRouteTopLevel = (menu) => {
  // 菜单名称、菜单地址
  const {
    resourceDesc,
    resource,
    resourceCode,
    pathDesc,
  } = menu;
  if (isExternalLink(resource)) {
    return null;
  }
  const { extendObj } = menu;
  const component = layoutModel(extendObj.layout ?? settings.pcLayout);
  // 如果该顶级菜单有子菜单
  if (!isEmpty(menu.children)) {
    return {
      name: resourceCode,
      path: resource,
      component,
      meta: { hasAuth: true, breadcrumb: pathDesc, ...extendObj },
    };
  }
  return {
    component,
    path: `${resource}/index`,
    children: [{
      path: resource,
      component: appMainModel,
      name: resourceCode,
      meta: {
        pathId: menu.id,
        title: resourceDesc,
        hasAuth: true,
        breadcrumb: pathDesc,
        ...extendObj,
      }
    }]
  };
};

/**
 * 根据菜单构建动态路由.
 * 构建方式：1.遍历第一级菜单，将第一级菜单构建的路由作为父路由
 * 2.1. 如果第一级菜单有子菜单，则将多层嵌套的子菜单展平为一维的子菜单
 * 2.2. 遍历展平后的子菜单，构建菜单路由
 * 2.3. 将构建完的子路由数组，作为父路由的chidren。
 * 3.1. 如果第一级菜单没有子菜单，则该页面不展示sidebar
 * @param {Array} menus 菜单列表
 * @returns {Array} routes 动态路由数组
 */
const constructLevelDynamicMenuRoutes = (menus = []) => menus.map((menu) => {
  // 构建第一层route
  const topRoute = constructRouteTopLevel(menu);
  // 如果当前菜单有子菜单
  if (topRoute && !isEmpty(menu.children)) {
    // 根据子菜单列表构建第一层路由的子路由，将子路由放到顶级菜单路由的children对象中
    topRoute.children = constructChildrenRoutes(menu).filter(Boolean);
  }
  return topRoute;
});

/**
 * 构建侧边栏导航.
 * 将一级菜单作为对象的key，一级菜单的children作为value，构建一个侧边栏菜单对象.
 * @param {Array} menus 获取到的菜单列表
 * @returns {Object} sideObj
 */
const constructSidebars = (menus = []) => transform(menus, (obj, v) => {
  // eslint-disable-next-line no-param-reassign
  obj[v.resourceCode] = v.children ?? [];
  return obj;
});

/**
 * 展开所有的menus
 */
export const flatMapMenus = (menus = []) => menus.reduce((acc, t) => [...(acc ?? []),
  ...(t.children ? flatMapMenus(t.children) : []), ...[t]], []);

/**
 * 遍历菜单，将每个菜单中的extendConfig从JSON字符串解析为对象，并将父菜单的extendConfig与子菜单的extendConfig合并
 * @param {Array} menus 菜单列表
 */
const dealMenuExtend = (menus, parentMenu) => map(menus, (item) => {
  // 当前菜单的extendObj
  const curr = {
    ...item,
    extendObj: {
      ...parentMenu?.extendObj,
      ...getMenuExtend(item.extendConfig),
    },
  };
  return {
    ...curr,
    children: dealMenuExtend(item.children, curr),
  };
});

/**
 * 根据菜单类型渲染不同的layout
 * @param {Array} menus 菜单列表
 * @param {Object} router router对象
 * @param {Object} store vuex store
 */
const constructMenus = (menus, store) => {
  const config = {
    'sidebar-only': () => {
      const [draftMenus, otherMenus] = partition(menus, (t) => t.extendConfig?.includes('isDraft'));
      // 更改菜单加载状态
      store.commit('menu/MENU_ROUTE_LOADED', true);
      store.commit('menu/DRAFT_MENUS', draftMenus);
      store.commit('menu/MENUS', otherMenus);
    },
    'navbar-sidebar': () => {
      // 构建横向导航菜单
      const sidebars = constructSidebars(menus);
      const navbars = menus.map(t => ({
        ...t,
        children: [],
        hasChildren: false
      }));
      store.commit('menu/MENU_ROUTE_LOADED', true);
      store.commit('menu/NAV_BAR', navbars);
      store.commit('menu/SIDEBAR_TREE', sidebars);
      store.commit('menu/MENUS', menus);
    },
    'navbar-only': () => {
      // 更改菜单加载状态
      store.commit('menu/MENU_ROUTE_LOADED', true);
      store.commit('menu/MENUS', menus);
    },
    workbench: () => {
      console.log(menus);

      // 更改菜单加载状态
      store.commit('menu/MENU_ROUTE_LOADED', true);
      store.commit('menu/MENUS', menus);
    }
  };
  return config[settings.pcLayout]();
};

/**
 * 去除menu中children中的扩展数据为templateCode的部分
 */
export const filterTemplateCode = (menus) => menus.map(t => ({
  ...t,
  children: filterTemplateCode(flatMap(t.children)).some(v => v.extendConfig?.includes('templateCode'))
    ? [] : filterTemplateCode(flatMap(t.children))
}));

/**
 * 构建动态路由.
 * @param {Object} router 路由信息
 * @param {Object} store vuex/store
 */
export const constructRoutes = async (router, store, to) => {
  try {
    const res = await new AuthorityService(bizServiceURL).getUserResources();
    // 解析顶级菜单，将extendConfig解析为对象
    const menus = dealMenuExtend(res);
    // 获取展平的菜单列表
    const allMenus = flatMapMenus(menus);
    store.commit('menu/ALL_MENUS', allMenus);
    // 构建动态路由
    const routes = constructLevelDynamicMenuRoutes(menus).filter(Boolean);
    routes.forEach(t => {
      router.addRoute(t);
    });
    // 获取当前访问的路由所属菜单
    const currentMenu = allMenus.find((t) => t.resource === to.path);
    if (currentMenu) {
      // 获取当前菜单所属模块
      const { extendObj: { module } } = currentMenu;
      // 如果当前菜单配置了模块，则获取对应模块的菜单，否则获取所有没有配置模块的菜单
      const menusByModule = module
        ? menus.filter((t) => t.extendObj.module === module)
        : menus.filter((t) => !t.extendObj.module);
      // 构建菜单
      constructMenus(menusByModule, store);
      return menusByModule;
    }
    constructMenus(menus, store);
    return menus;
  } catch (error) {
    alertErrorMsg(error);
  }
};

export default {};
